//
// @Auther: by wujehy 
// @Email:wujehy@qq.com
// @Data:2020/9/12
// @Time:9:35 上午
//
//

#include <iostream>
#include "ClientService.h"
#include "boost/asio.hpp"
#include "glog/logging.h"

#include <NetworkProtocol.h>
#include "Common.h"
using tcp = boost::asio::ip::tcp;
namespace GeeJoan
{
    std::string ResposeDataPackageToString(ResposeDataPackage &resposeDataPackage);

}
using namespace GeeJoan;

void ClientService::do_connect(const boost::asio::ip::basic_resolver<boost::asio::ip::tcp, boost::asio::executor>::results_type &endpoints)
{
    LOG( INFO ) << " ClientService::do_connect ";
    boost::asio::async_connect(
            client, endpoints,
            [this](boost::system::error_code ec, tcp::endpoint)
            {
                LOG( INFO ) << " do_connect ";
                if (!ec)
                {
                    do_operations();
                    threadPool.appendTask(
                            [&]()
                            {
                                if (connect_success_callback)connect_success_callback();
                            } );

                    LOG( INFO ) << "call success start ";
                } else
                {
                    LOG( INFO ) << " link fail";
                    do_reconnect();
                    //                    this->io_context.stop();
                }
            } );
}

void ClientService::do_read(boost::system::error_code &ec)
{
    if (std::size_t len = client.read_some( boost::asio::buffer( data_ ), ec ))
    {
        write_buffer_ = boost::asio::buffer( data_, len );
        std::string message((char *) write_buffer_.data(), write_buffer_.size());

        // 发送到任务池处理

        if (message.length() < 2)
        {
            LOG( INFO ) << " un know type ";
            return;
        }


        threadPool.appendTask(
                [=]()
                {
                    std::unique_ptr<ResposeDataPackage> dataPackage = std::make_unique<ResposeDataPackage>();
                    dataPackage->modtype = message.at( 0 );
                    dataPackage->subtype = message.at( 1 );
                    dataPackage->ctaskid = message.at( 2 );
                    dataPackage->code = message.at( 3 );
                    Buffer_Len_DataType dataType;
                    dataType.len8[1] = message.at(4);
                    dataType.len8[0] = message.at(5);
                    dataPackage->len = dataType.len16;
                    LOG(INFO)<<"dataType.len16 = " <<dataType.len16;
                    dataPackage->message = message.substr( NETWORKPACKAGE_HEARDER_SIZE+1 , NETWORKPACKAGE_HEARDER_SIZE + dataType.len16 + 1);
                    this->modManager.rounte( std::move( dataPackage ));
                } );

    }
}

void ClientService::do_operations()
{
//    LOG( INFO ) << " do_read start ";
    client.async_wait(
            tcp::socket::wait_read,
            [this](boost::system::error_code ec)
            {

                if (!ec)
                {
                    do_read( ec );
                }


                if (!ec || ec == boost::asio::error::would_block)
                {
                    do_operations();
                } else
                {

                    LOG( INFO ) << " close id ";
                    client.close();
                    do_reconnect();
                }
            } );

}

void ClientService::init()
{
    tcp::resolver resolver( io_context );
    LOG( INFO ) << " ip = " << serveHost << " port " << port;
    auto endpoints = resolver.resolve( serveHost, port );
    do_connect( endpoints );;
}

int ClientService::send(uint8_t modType, uint8_t subtype, const std::string &msg, std::function<void(int)> callback)
{

    //TODO　标记这个包的id 左上下文回包　暂不处理
    uint8_t currentTaskid = (taskid++)%255;
    std::string reqMsg;
//    reqMsg.push_back(1);
    reqMsg.push_back( modType );
    reqMsg.push_back( subtype );
    reqMsg.push_back( currentTaskid );
    //　设置长度为 255 一个 uint8 长度
    if(reqMsg.length() > NETWORKPACKAGE_MAX_BODY_BUFFER_LEN)
    {
        LOG(INFO)<<" messag too long max_len = "<<NETWORKPACKAGE_MAX_BODY_BUFFER_LEN;
        return Status_FAIL;
    }

    Buffer_Len_DataType dataType;
    dataType.len16 = msg.length();
    reqMsg.push_back( dataType.len8[1]);
    reqMsg.push_back( dataType.len8[0]);
    reqMsg.append( msg );
    int64_t  currentTime = GeeJoan::Common::getTimeMicro();
    LOG(INFO)<<" send taskid = " <<int(currentTaskid) <<" send time = "<< currentTime;
    boost::asio::post(
            io_context,
            [this, reqMsg, callback]()
            {
//                LOG( INFO ) << " send doing msg = " << reqMsg;
                bool write_in_progress = !write_deque.empty();

//                LOG( INFO ) << " send doing write_in_progress = " << write_in_progress;
                write_deque.push_back( reqMsg );
                if (!write_in_progress)
                {
//                    LOG( INFO ) << " call send doing";
                    if (callback)callback( Status_SUCCESS );
                    do_write();
                } else
                {
//                    LOG( INFO ) << " call send doing fail";
                    if (callback)callback( Status_FAIL );
//                    do_reconnect();
                }
            } );
    return 0;
}

void ClientService::do_write()
{
//    LOG( INFO ) << "do write ";
    boost::asio::async_write(
            client,
            boost::asio::buffer( write_deque.front().data(),
                                 write_deque.front().length()),
            [this](boost::system::error_code ec, std::size_t /*length*/)
            {
                if (!ec)
                {
                    write_deque.pop_front();
                    if (!write_deque.empty())
                    {
                        LOG( INFO ) << " do do_write ";
                        do_write();
                    }
                } else
                {
                    client.close();
                }
            } );
}

void ClientService::close()
{
    boost::asio::post( io_context, [this]() { client.close(); } );
}

ClientService::ClientService(boost::asio::io_context &io_context_, int threadSize)
        : io_context( io_context_ ),
          client( io_context ), threadPool( threadSize )
{
    LOG( INFO ) << " init client ";
    modManager.init();
    threadPool.start();
//    do_connect(endpoints);;
}

void ClientService::do_reconnect()
{
    // TODO wujehy 重联 , 搜索其他节点 暂时不做
    // 找到其他节点然后 从其他节点找到真正的新主服务器
    LOG( INFO ) << " reconnect doing ...";
    init();
    std::this_thread::sleep_for( std::chrono::seconds( 5 ));
}

void ClientService::setServeHost(const std::string &serveHost)
{
    ClientService::serveHost = serveHost;
}

void ClientService::setPort(const std::string &port)
{
    ClientService::port = port;
}

bool ClientService::registerModFunc(BaseClientMod *mod, bool auto_del)
{
    return modManager.registerModFunc(mod , auto_del);
}

void ClientService::reset()
{
    send( ModType_AuthMod, OpAuthModSubType_Reset, "", [](int status)
    {
        LOG( INFO ) << " ac send";
    } );
}

int ClientService::clientid()
{
    return id;
}

ClientService::~ClientService()
{
    threadPool.stop();
}

std::string GeeJoan::ResposeDataPackageToString(ResposeDataPackage &resposeDataPackage)
{
    std::stringstream ss;
    ss << " modtype = " << int( resposeDataPackage.modtype )
       << ",subtype = " << int( resposeDataPackage.subtype )
       << ",ctaskid = " << int( resposeDataPackage.ctaskid )
       << ",code = " << int( resposeDataPackage.code )
       << ",len = " << int( resposeDataPackage.len );
    return ss.str();
}